package com.demo.es;

import com.alibaba.fastjson.JSONObject;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.sort.SortOrder;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 描述: elasticSearch    api调用
 *
 * @author : lhb
 * @date : 2019-10-29 14:19
 */
public class EsApi {

    /**
     * 索引即我们的新增操作，ES提供了三种形式的索引，
     * 分别为通过Json字符串、Map集合、XContentBuilder实现索引操作
     */
    public void create(RestHighLevelClient client) {
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("user");
        createIndexRequest.mapping(
                "{\n" +
                        "  \"properties\": {\n" +
                        "    \"opTime\": {\n" +
                        "      \"type\": \"date\",\n" +
                        "      \"format\": \"yyyy-MM-dd HH:mm:ss\"\n" +
                        "    }\n" +
                        "  }\n" +
                        "}",
                XContentType.JSON);

        try {
            CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------创建索引---------------------");
            System.out.println(createIndexResponse.index());
        } catch (IOException e) {
            e.printStackTrace();
        }

        IndexRequest indexRequest = new IndexRequest("user");
        User user = new User();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(new Date());
        user.setOpTime(dateString);
        String json = JSONObject.toJSONString(user);
        indexRequest.source(json, XContentType.JSON);

        try {
            IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------根据创建索引，插入数据---------------------");
            System.out.println(indexResponse.status());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 插入数据，没有索引即创建
     */
    public void insert(RestHighLevelClient client) {
        //Json字符串作为数据源
        IndexRequest request = new IndexRequest("testpage");
        String jsonString = "{" +
                "\"name\":\"生命的诞生\"," +
                "\"type\":\"科学\"," +
                "\"price\":\"170\"" +
                "}";
        request.source(jsonString, XContentType.JSON);

        try {
            IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
            System.out.println("--------------------创建索引---------------------");
            System.out.println(indexResponse.status().equals(RestStatus.CREATED));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 插入超过10000条数据，没有索引即创建
     */
    public void insertOverData(RestHighLevelClient client) {
        //Json字符串作为数据源
        BulkRequest bulkRequest = new BulkRequest();

        for (int i = 1; i <= 10002; i++) {
            IndexRequest indexRequest = new IndexRequest("testpage");
            String jsonString = "{" +
                    "\"num\":" + i + "," +
                    "\"name\":\"生命的诞生\"," +
                    "\"type\":\"科学\"," +
                    "\"price\":\"170\"" +
                    "}";
            indexRequest.source(jsonString, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }

        try {
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------创建10000条数据---------------------");
            System.out.println(bulkResponse.status());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 批量插入文档
     */
    public void bulk(RestHighLevelClient client) {
        //Json字符串作为数据源
        BulkRequest bulkRequest = new BulkRequest();

        IndexRequest indexRequest = new IndexRequest("posts");
        String jsonString = "{" +
                "\"name\":\"生命的诞生\"," +
                "\"type\":\"科学\"," +
                "\"price\":\"170\"" +
                "}";
        indexRequest.source(jsonString, XContentType.JSON);

        bulkRequest.add(indexRequest);
        bulkRequest.add(indexRequest);
        bulkRequest.add(indexRequest);

        try {
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------创建索引---------------------");
            System.out.println(bulkResponse.status());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void get(RestHighLevelClient client) {

        CountRequest countRequest = new CountRequest();
        countRequest.indices("cmdb-iaas-logic-resource-ta_res_ip_address");
        try {
            CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------获取文档---------------------");
            System.out.println(countResponse.getCount());

        } catch (IOException e) {
            e.printStackTrace();
        }
//        GetRequest getRequest = new GetRequest(
//                "posts",
//                "3");
//        try {
//            GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
//            System.out.println("--------------------获取文档---------------------");
//            System.out.println(getResponse.getSource().toString());
//            
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
    }

    /**
     * 搜索数据
     */
    public void search(RestHighLevelClient client) {
        SearchRequest searchRequest = new SearchRequest(
                "user");

        // 2、用SearchSourceBuilder来构造查询请求体 ,请仔细查看它的方法，构造各种查询的方法都在这。
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //查询所有的数据
//        sourceBuilder.query(QueryBuilders.matchAllQuery());

        //查询出来所有包含name字段且name字段包含 生命的诞生值 的文档 模糊查询
//        sourceBuilder.query(QueryBuilders.termQuery("name", "生"));

        //termQuery精确查询
        //时间范围的设定
        RangeQueryBuilder rangequerybuilder = QueryBuilders
                .rangeQuery("opTime")
                .from("2019-10-13 11:19:20").to("2019-12-13 16:19:16");
        //生成DSL查询语句
        sourceBuilder.query(rangequerybuilder);
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("_id", "jSPFYm4Bu2wlTyaXv1jG");
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(rangequerybuilder).must(termQueryBuilder);

        sourceBuilder.query(boolQueryBuilder);
        //分页
        sourceBuilder.from(0);
        sourceBuilder.size(10);
        //将请求体加入到请求中
        searchRequest.source(sourceBuilder);
        try {
            //3、发送请求
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------搜索文档---------------------");
            System.out.println(searchResponse.getHits().getHits().length);
            System.out.println(Arrays.toString(searchResponse.getHits().getHits()));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void exists(RestHighLevelClient client) {

        GetRequest getRequest = new GetRequest("posts");
        try {
            //禁用获取 _source字段
            getRequest.fetchSourceContext(new FetchSourceContext(false));
            //禁用获取存储字段。
            getRequest.storedFields("_none_");
            boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------判断文档是否存在-------------");
            System.out.println(exists);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void update(RestHighLevelClient client) {

        UpdateRequest updateRequest = new UpdateRequest(
                "posts",
                "_doc",
                "3");

        String jsonString = "{" +
                "\"name\":\"进化论\"," +
                "\"type\":\"进化\"," +
                "\"price\": 1" +
                "}";
        updateRequest.doc(jsonString, XContentType.JSON);
        try {
            UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------更新文档---------------------");
            System.out.println(updateResponse.status().equals(RestStatus.OK));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void delete(RestHighLevelClient client) {

        DeleteRequest deleteRequest = new DeleteRequest(
                "posts",
                "_doc",
                "1");
        deleteRequest.timeout(TimeValue.timeValueMinutes(10));
        deleteRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        try {
            DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
            System.out.println("--------------------删除文档---------------------");
            System.out.println(deleteResponse.status());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用滚屏Scroll搜索文档，文档数大于10000
     * 不适用实时性查询场景
     *
     * @throws IOException es异常
     */
    public void testpage(RestHighLevelClient client) throws IOException {

        System.out.println("--------------------使用滚屏Scroll搜索文档---------------------");
        SearchRequest searchRequest = new SearchRequest(
                "testpage");
        //设置滚动对象，并设置游标过期时间
        Scroll scroll = new Scroll(TimeValue.timeValueSeconds(60));
        searchRequest.scroll(scroll);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //设置查询大小
        searchSourceBuilder.size(5000);
        searchRequest.source(searchSourceBuilder);

        List<Map<String, Object>> result = new ArrayList<>();

        //设置游标
        String scrollId;
        //3、发送请求
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        do {
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                //获取需要数据
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                result.add(sourceAsMap);
            }
            //每次循环完后取得scrollId,用于记录下次将从这个游标开始取数
            scrollId = searchResponse.getScrollId();
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
            scrollRequest.scroll(scroll);
            //进行下次查询
            searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
        } while (searchResponse.getHits().getHits().length != 0);
        //清除滚屏
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
        //也可以选择setScrollIds()将多个scrollId一起使用
        clearScrollRequest.addScrollId(scrollId);
        client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
        System.out.println("查询的总条数" + result.size());
        System.out.println(result);
    }

    /**
     * 使用search_after搜索文档，文档数大于10000
     * 通过唯一排序值定位,可并行的拉取大量数据
     *
     * @throws IOException es异常
     */
    public void testSearchAfter(RestHighLevelClient client) throws IOException {

        System.out.println("--------------------使用search_after搜索文档---------------------");
        SearchRequest searchRequest = new SearchRequest(
                "testpage");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //设置查询大小
        searchSourceBuilder.size(2);
        //设置唯一排序值定位
        searchSourceBuilder.sort("num", SortOrder.ASC);
        searchRequest.source(searchSourceBuilder);

        List<Map<String, Object>> result = new ArrayList<>();

        //3、发送请求
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        do {
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                //获取需要数据
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                result.add(sourceAsMap);
            }

            //取得最后的排序值sort,用于记录下次将从这个地方开始取数
            SearchHit[] hits = searchResponse.getHits().getHits();
            Object[] lastNum = hits[hits.length - 1].getSortValues();

            //设置searchAfter的最后一个排序值
            searchSourceBuilder.searchAfter(lastNum);
            searchRequest.source(searchSourceBuilder);

            //进行下次查询
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } while (searchResponse.getHits().getHits().length != 0);
        System.out.println("查询的总条数" + result.size());
        System.out.println(result);
    }

}
